package aishua.com.mytestapplist.apdu;

import android.content.Context;


import org.simalliance.openmobileapi.Channel;
import org.simalliance.openmobileapi.Reader;
import org.simalliance.openmobileapi.SEService;
import org.simalliance.openmobileapi.Session;

import java.io.IOException;
import java.util.Properties;

import aishua.com.mytestapplist.utils.CommonMethods;
import aishua.com.mytestapplist.utils.LogUtils;

public class MyApduAdapter {
	private static final String TAG = "MyApduAdapter-";
	private static MyApduAdapter mGongJiaoAdatper;
	private Object waitSEServiceConnected;
	private Context mContext;
	private Channel mChannel;
	private SEService mSeService;
	private Session mSession;
	public static MyApduAdapter getDefaultAdapter(Context context) {

		if (mGongJiaoAdatper == null) {
			mGongJiaoAdatper = new MyApduAdapter(context);
		}
		return mGongJiaoAdatper;
	}
	
	private MyApduAdapter(Context context) {
		this.mContext = context.getApplicationContext();
	}
	
	public String initEdepAdapter(String aid) {
		LogUtils.v(TAG+"initEdepAdapter()");
		String getSelectResponse = "";
		if (null == mSeService) {
			mSeService = getConnectedSEService();
		}
//		mSeService = getConnectedSEService();
		if (null != mChannel) {
			mChannel.close();
			mChannel = null;
		}
		mChannel = openSIMChannel(mSeService, CommonMethods.str2bytes(aid));
		if (null == mChannel) {
			LogUtils.w(TAG+"initEdepAdapter()-mChannel == null");
			return null;
		} 

		getSelectResponse = CommonMethods.bytesToHexString(mChannel.getSelectResponse());
		LogUtils.v(TAG+"initEdepAdapter()-getSelectResponse="+getSelectResponse);
		return getSelectResponse;
	}
	public Session initEdepSession( ) {

		if (null == mSeService) {
			mSeService = getConnectedSEService();
		}
//		Session mSession = getmSession(mSeService);

	   return  mSession;
	}
	public String initEdepAdapterSH (String aid) throws Exception {
		LogUtils.v(TAG+"initEdepAdapterSH()");

		String getSelectResponse = "";
		if (null == mSeService) {
			mSeService = getConnectedSEService();
		}
//		mSeService = getConnectedSEService();
		if (null != mChannel) {
			mChannel.close();
			mChannel = null;
		}
		mChannel = openSIMChannelSH(mSeService, CommonMethods.str2bytes(aid));
		if (null == mChannel) {
			LogUtils.w(TAG+"initEdepAdapter()-mChannel == null");
			return null;
		}

		getSelectResponse = CommonMethods.bytesToHexString(mChannel.getSelectResponse());
		LogUtils.v(TAG+"initEdepAdapter()-getSelectResponse="+getSelectResponse);
		return getSelectResponse;

	}

	public void releaseEdepAdapter() {
		LogUtils.v(TAG+"releaseEdepAdapter()");
		if (mChannel != null) {
			
			if (!mChannel.isClosed()) {
				mChannel.close();
			}
			mChannel = null;
		}
		if (mSession != null) {
			
			if (!mSession.isClosed()) {
				mSession.close();
			}
			
			mSession = null;
		}
		if (mSeService != null) {
			if (mSeService.isConnected()) {
				mSeService.shutdown();
			}
			mSeService = null;
		}
		
		waitSEServiceConnected = null;
		mContext = null;
		mGongJiaoAdatper = null;
	}
	
	public void releaseEdepAdapterWithNotShutDownSeService() {
		LogUtils.v(TAG+"releaseEdepAdapterWithNotShutDownSeService()");
		if (mChannel != null) {
			
			if (!mChannel.isClosed()) {
				mChannel.close();
			}
			mChannel = null;
		}
		
		mContext = null;
		mGongJiaoAdatper = null;
	}
	
	
	public String sendApdu(String apduCmd) {
		LogUtils.v(TAG+"sendApdu()");
		byte[] bApduRes = sendApdu(CommonMethods.str2bytes(apduCmd));
		if (bApduRes == null) {
			printAPDULog(apduCmd, "apdu transmit error");
			return "";
		}
		String apduRes = CommonMethods.bytesToHexString(bApduRes);
		printAPDULog(apduCmd, apduRes);
		return apduRes;
	}
	
	private byte[] sendApdu(byte[] bApduCmd) {
		try {
			return mChannel.transmit(bApduCmd);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
//	上海得到的就是字节数组
	public String sendApdu4byte(byte[] apduCmd) {
		LogUtils.v(TAG+"sendApdu4byte()");
		byte[] bApduRes = sendApdu(apduCmd);
		if (bApduRes == null) {
			printAPDULog(CommonMethods.bytesToHexString(apduCmd), "apdu transmit error");
			return "";
		}
		String apduRes = CommonMethods.bytesToHexString(bApduRes);
		printAPDULog(CommonMethods.bytesToHexString(apduCmd), apduRes);
		return apduRes;
	}
	private void printAPDULog(String cmd, String res) {
		LogUtils.v(TAG+"printAPDULog()");
		LogUtils.v(TAG+"printAPDULog()-APDU CMD:" + cmd);
		LogUtils.v(TAG+"printAPDULog()-APDU RES:" + res);
	}
	
	private Channel openSIMChannel(SEService services, byte[] ssd_aid) {
		LogUtils.v(TAG+"openSIMChannel()");
		Reader reader = null;
		if (null == reader) {
			reader = getSimReader(services);
		}
		try {
			if (null == mSession) {
				mSession = reader.openSession();
			}
			Channel channel = mSession.openLogicalChannel(ssd_aid);
			mChannel = channel;
			return channel;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public Session getmSession( ) {

		LogUtils.v(TAG + "getmSession()");
		if (null == mSeService) {
			mSeService = getConnectedSEService();
		}
		Reader reader = null;
		if (null == reader) {
			reader = getSimReader(mSeService);
		}
		try {
			if (null == mSession) {

				mSession = reader.openSession();
			}

		} catch (IOException e) {
			e.printStackTrace();

		}
		return mSession;
	}

	private Channel openSIMChannelSH(SEService services, byte[] ssd_aid) throws Exception {
		LogUtils.v(TAG+"openSIMChannelSH()");
		Reader reader = null;
		if (null == reader) {
			reader = getSimReader(services);
		}

			if (null == mSession) {
				mSession = reader.openSession();
			}
			Channel channel = mSession.openLogicalChannel(ssd_aid);
			mChannel = channel;
			return channel;

	}

	private Reader getSimReader(SEService seService) {
	    Reader[] readers = seService.getReaders();
	    Reader simReader = null;
	    for (Reader reader : readers) {
	    	if (reader.getName().startsWith("SIM")) {
	    		simReader = reader;
	    		break;
	    	}
	    }
	    return simReader; 
	}

	private SEService getConnectedSEService() {
		
	    if (this.waitSEServiceConnected == null) {
	    	this.waitSEServiceConnected = new Object();
	    }

		SEService seService = new SEService(mContext,  new SEInfoServieCB());

	    for (int i = 0; i < 3; i++) {
	    	synchronized (this.waitSEServiceConnected) {
		        try { 
		        	this.waitSEServiceConnected.wait(2000);
		          	if (!seService.isConnected())
		          			continue;
		          	else 
		          		break;
		        } catch (InterruptedException e) {
		        	LogUtils.v(TAG+"waitSEServiceConnected interruptted! " 
		        			+ "But relax, it's Ok.");
		        }
	    	}
	    }

	    if (seService.isConnected()) {
	    	mSeService = seService;
	    	return seService;
	    }
	    return null;
	}
	
	private class SEInfoServieCB implements SEService.CallBack {
		  
		public SEInfoServieCB() {
			  
		}

		public void serviceConnected(SEService arg0) {
			synchronized (MyApduAdapter.this.waitSEServiceConnected) {
				MyApduAdapter.this.waitSEServiceConnected.notifyAll();
			}
		}
	}
	
	public String transmitApdu(String commString) {
		try {
			if(mChannel==null){

				throw new RuntimeException("the channel is null service close");
			}
			byte[] res = mChannel.transmit(CommonMethods.str2bytes(commString));
			return CommonMethods.bytesToHexString(res);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return "";
	}
}
